package br.com.orion.sga.controller;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;

import org.primefaces.context.RequestContext;
import org.primefaces.event.SelectEvent;

import br.com.orion.sga.exception.DaoException;
import br.com.orion.sga.model.entities.Aluno;
import br.com.orion.sga.model.entities.Banco;
import br.com.orion.sga.model.entities.Caixa;
import br.com.orion.sga.model.entities.Cheque;
import br.com.orion.sga.model.entities.FormaPagamento;
import br.com.orion.sga.model.entities.Funcionario;
import br.com.orion.sga.model.entities.MovimentacaoCaixa;
import br.com.orion.sga.model.entities.OperadoraCartao;
import br.com.orion.sga.model.entities.PagamentoCartao;
import br.com.orion.sga.model.entities.ParcelaPagamento;
import br.com.orion.sga.model.entities.PeriodoLiberacao;
import br.com.orion.sga.model.entities.TipoMovimentacao;
import br.com.orion.sga.service.BancoService;
import br.com.orion.sga.service.ChequeService;
import br.com.orion.sga.service.FormaPagamentoService;
import br.com.orion.sga.service.MovimentacaoCaixaService;
import br.com.orion.sga.service.OperadoraCartaoService;
import br.com.orion.sga.service.PagamentoCartaoService;
import br.com.orion.sga.service.ParcelaPagamentoService;
import br.com.orion.sga.service.PeriodoLiberacaoService;
import br.com.orion.sga.service.TipoMovimentacaoService;
import br.com.orion.sga.utils.MessageHandler;

@ManagedBean(name = "addPaymentController")
@ViewScoped
public class AddPaymentController {
	
	private static final int MENSALIDADE_ID = 1;
	private static final int MATRICULA_ID = 4;
	private static final int CHEQUE_ID = 4;
	private static final int CREDITO_ID = 3;
	
	
	/**
	 * Attributes
	 */
	private List<TipoMovimentacao> listOfTypeOfPayment;
	private List<FormaPagamento> listOfWayOfPayment;
	private List<Banco> listOfBanks;
	private List<Integer> listDiaVencimentoFaturaCartao;
	private List<OperadoraCartao> listOfCreditCardCarrier;
	private Date endPeriodFree;
	private float personalValue;
	private int diaPagamentoFaturaCartao;
	
	
	private MovimentacaoCaixa movimentacaoCaixa;
	private Aluno student;
	private Cheque cheque;
	private PagamentoCartao pagamentoCartao;
	
	/**
	 * Render Fields
	 */
	private boolean valueRendered;
	private boolean endPeriodFreeRendered;
	private boolean dateOfPaymentRendered;
	private boolean personalValueRendered;
	private boolean descriptionRendered;
	private boolean wayOfPaymentRendered;
	private boolean buttonSaveRendered;
	private boolean paymentByChequeRendered;
	private boolean paymentByCreditCard;
	
	/**
	 * Services
	 */
	@ManagedProperty("#{tipoMovimentacaoService}")
	private TipoMovimentacaoService tipoMovimentacaoService;
	
	@ManagedProperty("#{formaPagamentoService}")
	private FormaPagamentoService formaPagamentoService;
	
	@ManagedProperty("#{chequeService}")
	private ChequeService chequeService;
	
	@ManagedProperty("#{bancoService}")
	private BancoService bancoService;
	
	@ManagedProperty("#{operadoraCartaoService}")
	private OperadoraCartaoService operadoraCartaoService;
	
	@ManagedProperty("#{movimentacaoCaixaService}")
	private MovimentacaoCaixaService movimentacaoCaixaService;
	
	@ManagedProperty("#{parcelaPagamentoService}")
	private ParcelaPagamentoService parcelaPagamentoService;
	
	@ManagedProperty("#{pagamentoCartaoService}")
	private PagamentoCartaoService pagamentoCartaoService;
	
	@ManagedProperty("#{periodoLiberacaoService}")
	private PeriodoLiberacaoService periodoLiberacaoService;
	
	/**
	 * Construct
	 */
	@PostConstruct
	public void init() {
		initialize();
	}

	public void initialize() {
		movimentacaoCaixa = new MovimentacaoCaixa();
		movimentacaoCaixa.setTipoMovimentacao(new TipoMovimentacao());
		movimentacaoCaixa.setFormaPagamento(new FormaPagamento());
		valueRendered = false;
		personalValueRendered = false;
		endPeriodFreeRendered = false;
		dateOfPaymentRendered = false;
		descriptionRendered = false;
		wayOfPaymentRendered = false;
		buttonSaveRendered = false;
		paymentByChequeRendered = false;
		paymentByCreditCard = false;
	}
	
	private void populateCombo() {
		listOfTypeOfPayment = tipoMovimentacaoService.listAll();
		listOfWayOfPayment = formaPagamentoService.listAll();
	}
	
	/* Method that will call when users click on button to add a new cheque */
	public void openAddCheque() {
		cheque = new Cheque();
		cheque.setBanco(new Banco());
		listOfBanks = bancoService.listAll();
	}

	/* Method that will call when users select a option on way of type payment */
	public void openPaymentByCreditCard() {
		pagamentoCartao = new PagamentoCartao();
		pagamentoCartao.setOperadoraCartao(new OperadoraCartao());
		listOfCreditCardCarrier = operadoraCartaoService.listAll();
		listDiaVencimentoFaturaCartao = new ArrayList<>();
		
		/* Fill the list of day of payment from credit card */
		for (int i = 1; i < 32; i++) {
			listDiaVencimentoFaturaCartao.add(i);
		}
		RequestContext context = RequestContext.getCurrentInstance();
		context.execute("PF('dialogCreditCard').show();");
	}

	public void openPayment(Aluno student) {
		this.student = student;
		initialize();
		populateCombo();
	}
	
	public void typeOfPaymentChanged() {
		movimentacaoCaixa.setTipoMovimentacao(tipoMovimentacaoService.getById(movimentacaoCaixa.getTipoMovimentacao().getId()));
		if (movimentacaoCaixa.getTipoMovimentacao().getId() == MENSALIDADE_ID) {
			showFieldsForMensalidade();
			personalValueRendered = true;
			
		} else if (movimentacaoCaixa.getTipoMovimentacao().getId() == MATRICULA_ID) {
			showFieldsForMensalidade();
			personalValueRendered = false;
			
		} else {
			personalValueRendered = false;
			endPeriodFreeRendered = false;
			dateOfPaymentRendered = true;
			movimentacaoCaixa.setData(new Date());
			movimentacaoCaixa.setValor(0f);
			paymentByChequeRendered= false;
			 paymentByCreditCard = false;
		}
		valueRendered = true;
		descriptionRendered = true;
		wayOfPaymentRendered = true;
		buttonSaveRendered  = true;
		
		
	}

	/**
	 * Method that will call when user select the type of payment equal to Mensalidade or Matrícula
	 */
	private void showFieldsForMensalidade() {
		endPeriodFreeRendered = true;
		dateOfPaymentRendered = true;
		movimentacaoCaixa.setData(new Date());
		movimentacaoCaixa.setValor(student.getValorMensalidade());
		Calendar calendar = Calendar.getInstance();
		int day = student.getVencimento().getDia();
		int month = calendar.get(Calendar.MONTH);
		int year = calendar.get(Calendar.YEAR);
		if (month + 1 > 11) {
			month = 01;
			year++;
		} else {
			month++;//next Month
		}
		
		calendar.set(year, month, day);
		endPeriodFree = calendar.getTime();
		personalValue = student.getValorPersonal();
	}
	
	public void dateOfPaymentChanged(SelectEvent event){
	}
	
	
	public void wayOfPaymentChanged() {
		 if (movimentacaoCaixa.getFormaPagamento().getId() == CHEQUE_ID) {
			 paymentByChequeRendered= true;
			 paymentByCreditCard = false;
		 } else if (movimentacaoCaixa.getFormaPagamento().getId() == CREDITO_ID){
			 paymentByCreditCard = true;
			 paymentByChequeRendered = false;
			 openPaymentByCreditCard();
		 } else {
			 paymentByChequeRendered= false;
			 paymentByCreditCard = false;
		 }
		 buttonSaveRendered = true;
	}
	
	
	
	public void savePayment() {
		/* Correct the way of to get the CAIXA ID */
		Caixa caixa = new Caixa();
		caixa.setId(1l);
		movimentacaoCaixa.setCaixa(caixa);
		
		movimentacaoCaixa.setAluno(student);
		Funcionario func = new Funcionario();
		func.setId(12);
		movimentacaoCaixa.setFuncionario(func);
		
		if (movimentacaoCaixa.getFormaPagamento().getId() == CHEQUE_ID) { // Payment by Cheque
			for(Cheque iterator: movimentacaoCaixa.getCheques()) {
				iterator.setMovimentacaoCaixa(movimentacaoCaixa);
			}
			try {
				movimentacaoCaixaService.save(movimentacaoCaixa);
				
			} catch (DaoException e) {
				MessageHandler.placeErrorMessage("Erro ao salvar o pagamento por Cheque");
				return;
			}
			
		} else if (movimentacaoCaixa.getFormaPagamento().getId() == CREDITO_ID){ //Payment by Credit Card
			//Não entendi pq aqui funcionou ao contrário. Uma vez que está com o Cascade (1º salvo a movimentacaoCaixa e depois o PagamentoCartao)
			try {
				movimentacaoCaixaService.save(movimentacaoCaixa);
			} catch (DaoException e) {
				MessageHandler.placeErrorMessage("Erro ao salvar o pagamento por Cartão de Crédito");
				return;
			}
			pagamentoCartao.setMovimentacaoCaixa(movimentacaoCaixa);
			try {
				pagamentoCartaoService.save(pagamentoCartao);
			} catch (DaoException e) {
				MessageHandler.placeErrorMessage("Erro ao salvar pagamento");
				return;
			}
		} else { //Payment by Money or Debit Card
			try {
				movimentacaoCaixaService.save(movimentacaoCaixa);
			} catch (DaoException e) {
				MessageHandler.placeErrorMessage("Erro ao salvar pagamento");
				return;
			}
		}
		
		/* Check if the type of payment is equal to Mensalidade or Matrícula */
		if (movimentacaoCaixa.getTipoMovimentacao().getId() == MENSALIDADE_ID || 
			movimentacaoCaixa.getTipoMovimentacao().getId() == MATRICULA_ID) {
			setFreePeriodForStudent();
		} else {// Not necessary put the periodoLiberacao
			MessageHandler.placeWarningMessage("Pagamento realizado com sucesso!");
		}
		
	}
	
	/* Method that will called when the type of payment is equal to mensalidade and matricula */
	private void setFreePeriodForStudent() {
		PeriodoLiberacao period = new PeriodoLiberacao();
		period.setAluno(student);
		period.setPeriodo(endPeriodFree);
		Character status = 'A';
		period.setStatus(status);
		try {
			periodoLiberacaoService.save(period);
			MessageHandler.placeWarningMessage("Pagamento realizado com sucesso!");
		} catch (DaoException e) {
			MessageHandler.placeErrorMessage("Erro ao definir o periodo de adimplente do aluno!");
		}
	}

	public void saveCheque() {
		
		if (!isValidFormAddCheque()) {
			
		} else {
			movimentacaoCaixa.getCheques().add(cheque);
		}
		
		
	}
	
	/* check if the form to add a new cheque has its fields filled completely */
	private boolean isValidFormAddCheque() {
		if (cheque.getNumeroCheque() != null){
			if (cheque.getAgencia() != null) {
				if (cheque.getConta() != null) {
					if (cheque.getBomPara() != null) {
						return true;
					}
				}
			}
		}
		return false;
	}
	
	
	/* Call this method when the user select add Payment By Credit Card */
	public void addPaymentByCreditCard() {
		
		//reset the all parcelas
		pagamentoCartao.setParcelaPagamentos(new HashSet<ParcelaPagamento>());
		
		Calendar calendar = Calendar.getInstance();
		int monthForFirstPayment = calendar.get(Calendar.MONTH) + 1;
		//Check if the first payment must be on actual month or in the next
		if (diaPagamentoFaturaCartao + 7 > calendar.get(Calendar.DAY_OF_MONTH)) {
			monthForFirstPayment++;
		}
		int year = calendar.get(Calendar.YEAR);
		for (int i = 0; i < pagamentoCartao.getNumeroParcela(); i++) {
			int day = diaPagamentoFaturaCartao;
			
			if (monthForFirstPayment > 12) {
				monthForFirstPayment = 01;
				year = calendar.get(Calendar.YEAR) + 1;
			} else {
				if (i != 0) {//increase a month because i == 0 so the month is already increased
					monthForFirstPayment++;//next Month
				}
			}
			calendar.set(year, monthForFirstPayment, day);
			ParcelaPagamento parcelaPagamento = new ParcelaPagamento();
			parcelaPagamento.setVencimento(calendar.getTime());
			float valuePerPayment = movimentacaoCaixa.getValor() / pagamentoCartao.getNumeroParcela();
			parcelaPagamento.setValor(valuePerPayment);
			parcelaPagamento.setPagamentoCartao(pagamentoCartao);
			pagamentoCartao.getParcelaPagamentos().add(parcelaPagamento);
		}
		
	}

	/**
	 * GETTERS AND SETTERS 
	 */
	public List<TipoMovimentacao> getListOfTypeOfPayment() {
		return listOfTypeOfPayment;
	}
	
	public void setListOfTypeOfPayment(
			List<TipoMovimentacao> listOfTypeOfPayment) {
		this.listOfTypeOfPayment = listOfTypeOfPayment;
	}
	
	public TipoMovimentacaoService getTipoMovimentacaoService() {
		return tipoMovimentacaoService;
	}
	
	public void setTipoMovimentacaoService(
			TipoMovimentacaoService tipoMovimentacaoService) {
		this.tipoMovimentacaoService = tipoMovimentacaoService;
	}
	
	public MovimentacaoCaixa getMovimentacaoCaixa() {
		return movimentacaoCaixa;
	}
	
	public void setMovimentacaoCaixa(MovimentacaoCaixa movimentacaoCaixa) {
		this.movimentacaoCaixa = movimentacaoCaixa;
	}

	public boolean isValueRendered() {
		return valueRendered;
	}

	public void setValueRendered(boolean valueRendered) {
		this.valueRendered = valueRendered;
	}

	public boolean isEndPeriodFreeRendered() {
		return endPeriodFreeRendered;
	}

	public void setEndPeriodFreeRendered(boolean endPeriodFreeRendered) {
		this.endPeriodFreeRendered = endPeriodFreeRendered;
	}

	public boolean isDateOfPaymentRendered() {
		return dateOfPaymentRendered;
	}

	public void setDateOfPaymentRendered(boolean dateOfPaymentRendered) {
		this.dateOfPaymentRendered = dateOfPaymentRendered;
	}
	
	public Date getEndPeriodFree() {
		return endPeriodFree;
	}
	
	public void setEndPeriodFree(Date endPeriodFree) {
		this.endPeriodFree = endPeriodFree;
	}
	
	public Aluno getStudent() {
		return student;
	}
	
	public void setStudent(Aluno student) {
		this.student = student;
	}

	public float getPersonalValue() {
		return personalValue;
	}

	public void setPersonalValue(float personalValue) {
		this.personalValue = personalValue;
	}

	public boolean isPersonalValueRendered() {
		return personalValueRendered;
	}

	public void setPersonalValueRendered(boolean personalValueRendered) {
		this.personalValueRendered = personalValueRendered;
	}

	public boolean isDescriptionRendered() {
		return descriptionRendered;
	}

	public void setDescriptionRendered(boolean descriptionRendered) {
		this.descriptionRendered = descriptionRendered;
	}

	public List<FormaPagamento> getListOfWayOfPayment() {
		return listOfWayOfPayment;
	}

	public void setListOfWayOfPayment(List<FormaPagamento> listOfWayOfPayment) {
		this.listOfWayOfPayment = listOfWayOfPayment;
	}

	public boolean isWayOfPaymentRendered() {
		return wayOfPaymentRendered;
	}

	public void setWayOfPaymentRendered(boolean wayOfPaymentRendered) {
		this.wayOfPaymentRendered = wayOfPaymentRendered;
	}
	
	public FormaPagamentoService getFormaPagamentoService() {
		return formaPagamentoService;
	}
	
	public void setFormaPagamentoService(
			FormaPagamentoService formaPagamentoService) {
		this.formaPagamentoService = formaPagamentoService;
	}

	public boolean isButtonSaveRendered() {
		return buttonSaveRendered;
	}

	public void setButtonSaveRendered(boolean buttonSaveRendered) {
		this.buttonSaveRendered = buttonSaveRendered;
	}

	public boolean isPaymentByChequeRendered() {
		return paymentByChequeRendered;
	}

	public void setPaymentByChequeRendered(boolean paymentByChequeRendered) {
		this.paymentByChequeRendered = paymentByChequeRendered;
	}

	public Cheque getCheque() {
		return cheque;
	}

	public void setCheque(Cheque cheque) {
		this.cheque = cheque;
	}
	
	public ChequeService getChequeService() {
		return chequeService;
	}
	
	public void setChequeService(ChequeService chequeService) {
		this.chequeService = chequeService;
	}
	
	public BancoService getBancoService() {
		return bancoService;
	}
	
	public void setBancoService(BancoService bancoService) {
		this.bancoService = bancoService;
	}
	
	public List<Banco> getListOfBanks() {
		return listOfBanks;
	}
	
	public void setListOfBanks(List<Banco> listOfBanks) {
		this.listOfBanks = listOfBanks;
	}
	
	public PagamentoCartao getPagamentoCartao() {
		return pagamentoCartao;
	}
	
	public void setPagamentoCartao(PagamentoCartao pagamentoCartao) {
		this.pagamentoCartao = pagamentoCartao;
	}
	
	public OperadoraCartaoService getOperadoraCartaoService() {
		return operadoraCartaoService;
	}
	
	public void setOperadoraCartaoService(
			OperadoraCartaoService operadoraCartaoService) {
		this.operadoraCartaoService = operadoraCartaoService;
	}
	
	public List<OperadoraCartao> getListOfCreditCardCarrier() {
		return listOfCreditCardCarrier;
	}
	
	public void setListOfCreditCardCarrier(
			List<OperadoraCartao> listOfCreditCardCarrier) {
		this.listOfCreditCardCarrier = listOfCreditCardCarrier;
	}
	
	public List<Integer> getListDiaVencimentoFaturaCartao() {
		return listDiaVencimentoFaturaCartao;
	}
	
	public void setListDiaVencimentoFaturaCartao(
			List<Integer> listDiaVencimentoFaturaCartao) {
		this.listDiaVencimentoFaturaCartao = listDiaVencimentoFaturaCartao;
	}
	
	public int getDiaPagamentoFaturaCartao() {
		return diaPagamentoFaturaCartao;
	}
	
	public void setDiaPagamentoFaturaCartao(int diaPagamentoFaturaCartao) {
		this.diaPagamentoFaturaCartao = diaPagamentoFaturaCartao;
	}

	public boolean isPaymentByCreditCard() {
		return paymentByCreditCard;
	}

	public void setPaymentByCreditCard(boolean paymentByCreditCard) {
		this.paymentByCreditCard = paymentByCreditCard;
	}
	
	public MovimentacaoCaixaService getMovimentacaoCaixaService() {
		return movimentacaoCaixaService;
	}
	
	public void setMovimentacaoCaixaService(
			MovimentacaoCaixaService movimentacaoCaixaService) {
		this.movimentacaoCaixaService = movimentacaoCaixaService;
	}
	
	public ParcelaPagamentoService getParcelaPagamentoService() {
		return parcelaPagamentoService;
	}
	
	public void setParcelaPagamentoService(
			ParcelaPagamentoService parcelaPagamentoService) {
		this.parcelaPagamentoService = parcelaPagamentoService;
	}
	
	public PagamentoCartaoService getPagamentoCartaoService() {
		return pagamentoCartaoService;
	}
	
	public void setPagamentoCartaoService(
			PagamentoCartaoService pagamentoCartaoService) {
		this.pagamentoCartaoService = pagamentoCartaoService;
	}
	
	public PeriodoLiberacaoService getPeriodoLiberacaoService() {
		return periodoLiberacaoService;
	}
	
	public void setPeriodoLiberacaoService(
			PeriodoLiberacaoService periodoLiberacaoService) {
		this.periodoLiberacaoService = periodoLiberacaoService;
	}
	
}
